﻿using System;
using System.Collections.Generic;
using System.Text;

//using OpenTK;
using OpenTK.Graphics.OpenGL;
using OpenTK.Input;
using System.Drawing;
using SalmonViewer;
using OpenTK.Math;
using OpenTK.Graphics;
using OpenTK;
using OpenTK.Platform;

namespace GameOpenGL.Main
{
    class OpenTKSystem : OpenTK.GameWindow, IEngineSystem
    {
        //OpenTK.Graphics.TextPrinter printer;
        Font onScreenFont;
        ShaderManager shaderManager;

        Camera camera;

        double totalTime = 0;

        ThreeDSFile file;

        public OpenTKSystem() : base (1024, 768, OpenTK.Graphics.GraphicsMode.Default)
//		    new GraphicsMode(new ColorFormat( 8, 8, 8, 8 ), 16 ), "OpenGL 3.1 Example", 0,
//            DisplayDevice.Default, 3, 1, // use the default display device, request a 3.1 OpenGL context
//            GraphicsContextFlags.Debug ) //this will help us track down bugs
		{
            //Initialize();
        }

        private void Initialize()
        {
            this.Width = 1024;
            this.Height = 768;

            this.Title = "OpenGL app";

            //this.printer = new TextPrinter();
            this.onScreenFont = new Font(FontFamily.GenericMonospace, 14);

            this.camera = new Camera();
            this.shaderManager = new ShaderManager();

            Mouse.Move += MouseMove;
        }

		void MouseMove (object sender, MouseMoveEventArgs e)
		{
			this.camera.LookAround(e.XDelta, e.YDelta);
		}
		
		/// <summary>Load resources here.</summary>
        /// <param name="e">Not used.</param>
        public override void OnLoad(EventArgs e)
        {
            OpenTK.Graphics.OpenGL.GL.ClearColor(System.Drawing.Color.SteelBlue);
            OpenTK.Graphics.OpenGL.GL.Enable(OpenTK.Graphics.OpenGL.EnableCap.DepthTest);
			
			//GL.Enable(EnableCap.Light0);
			//GL.Enable(EnableCap.Lighting);
			OpenTK.Graphics.OpenGL.GL.ShadeModel(OpenTK.Graphics.OpenGL.ShadingModel.Smooth);
			//GL.Enable(EnableCap.Fog);
			OpenTK.Graphics.OpenGL.GL.LightModel(OpenTK.Graphics.OpenGL.LightModelParameter.LightModelAmbient, new float[4] { 0.5f,0.5f,0.5f,0.5f });
			this.Context.VSync = false;
			
			string sep = System.IO.Path.DirectorySeparatorChar.ToString();
			//this.file = new ThreeDSFile(string.Format("Resources{0}3dm-sword{0}3dm-sword.3ds", sep));
			this.file = new ThreeDSFile(string.Format("Resources{0}house_nice{0}house_nice.3ds", sep));
			//this.file = new ThreeDSFile(string.Format("Resources{0}Male1_3ds{0}Male1.3ds", sep));
			
			//this.file = new ThreeDSFile(string.Format("Resources{0}City1{0}city.3ds", sep));
			
			this.shaderManager.LoadShader(string.Format("Resources{0}Shaders{0}basic_vertex_shader.fx", sep));
			this.shaderManager.LoadShader(string.Format("Resources{0}Shaders{0}basic_fragment_shader.fx", sep));
			
			this.shaderManager.CreateShaders(); 
            this.shaderManager.CreateProgram();
 
            //this.shaderManager.QueryMatrixLocations();
 
            float widthToHeight = Width / ( float )Height;
            this.shaderManager.SetProjectionMatrix( OpenTK.Matrix4.Perspective( 1.3f, widthToHeight, 1, 20 ) );

            this.shaderManager.SetModelviewMatrix(OpenTK.Matrix4.RotateX(0.5f) * OpenTK.Matrix4.CreateTranslation(0, 0, -4));

            OpenTK.Graphics.OpenGL.GL.UseProgram(this.shaderManager.ShaderProgramHandle);

			Logging.Logger.AddInfo("load complete");
        }

        /// <summary>
        /// Called when your window is resized. Set your viewport here. It is also
        /// a good place to set up your projection matrix (which probably changes
        /// along when the aspect ratio of your window).
        /// </summary>
        /// <param name="e">Contains information on the new Width and Size of the GameWindow.</param>
        public override void OnResize(ResizeEventArgs e)
        {
            OpenTK.Graphics.OpenGL.GL.Viewport(0, 0, Width, Height);
            OpenTK.Graphics.OpenGL.GL.MatrixMode(OpenTK.Graphics.OpenGL.MatrixMode.Projection);
            OpenTK.Graphics.OpenGL.GL.LoadIdentity();
            Glu.Perspective(45.0, Width / (double)Height, 1.0, 400.0);
        }

        /// <summary>
        /// Called when it is time to setup the next frame. Add you game logic here.
        /// </summary>
        /// <param name="e">Contains timing information for framerate independent logic.</param>
        public override void OnUpdateFrame(UpdateFrameEventArgs e)
        {
            if (Keyboard[Key.Escape])
                Exit();
			else if (Keyboard[Key.W])
				this.camera.Move(CameraMove.Forward);
			else if (Keyboard[Key.S])
				this.camera.Move(CameraMove.Back);
			else if (Keyboard[Key.A])
				this.camera.Move(CameraMove.Left);
			else if (Keyboard[Key.D])
				this.camera.Move(CameraMove.Right);
        }

        /// <summary>
        /// Called when it is time to render the next frame. Add your rendering code here.
        /// </summary>
        /// <param name="e">Contains timing information.</param>
        protected override void OnRenderFrame(UpdateFrameEventArgs e)
		{
			this.totalTime += e.Time;
            OpenTK.Graphics.OpenGL.GL.Clear(OpenTK.Graphics.OpenGL.ClearBufferMask.ColorBufferBit |
				OpenTK.Graphics.OpenGL.ClearBufferMask.DepthBufferBit);

            OpenTK.Graphics.OpenGL.GL.MatrixMode(OpenTK.Graphics.OpenGL.MatrixMode.Modelview);
			
			this.camera.UpdateCamera(e.Time);
			
			//GL.Scale(0.1f, 0.1f, 0.1f);			

			this.file.Model.Render();
			
            //GL.End();
            
            //printer.Begin();
            
            //printer.Print(((int)(1 / e.Time)).ToString("F0"), onScreenFont, Color.SpringGreen);
            ////printer.Print(this.camera.Position.ToString(), onScreenFont, Color.Red);
            
            //printer.End();

            OpenTK.Graphics.OpenGL.GL.Flush();
            SwapBuffers();
        }

        #region IEngineRunner Members

        public void Init()
        {
            Initialize();
        }

        public void Start()
        {
            this.Run(30,60);
        }

        public void Quit()
        {
            this.Dispose();
        }
        #endregion
    }
	
}
